<?php
/**
 * Наследник класса формирования HTML формы. Необходим для работы с правами дос-
 * тупа на основе кармы (рейтинга) пользователя.
 */
class Workset_Form extends ZendX_JQuery_Form {

    const ERROR_CODE = 10001;
    
	/**
	 * @var boolean
	 */
	protected $_bFormIsReadOnly = false;

	/**
	 * @var Zend_Acl
	 */
	protected $_oAcl = null;

	/**
	 * @var mixed
	 */
	protected $_oUser = null;

    /**
     * Списки элементов формируемые на стороне клиента
     */
    protected $_listElements = array();

    /**
     * Инициализация объекта класса.
     * 
     * @param array $options
     */
	public function __construct($options = array()) {
        if (empty($options['elementPrefixPath'])) {
           $options['elementPrefixPath'] = array(
                'filter' => array(
                    'prefix' => 'Workset_Filter',
                    'path' => 'Workset/Filter'
                ),
                'validate' => array(
                    'prefix' => 'Workset_Validate',
                    'path' => 'Workset/Validate'
                ),
                'decorator' => array(
                    'prefix' => 'Workset_Form_Decorator',
                    'path' => 'Workset/Form/Decorator'
            	),
            );
        }

		if (empty($options['prefixPath'])) {
            $options['prefixPath'] = array(
                'decorator' => array(
                    'prefix' => 'Workset_Form_Decorator',
                    'path' => 'Workset/Form/Decorator'
                ),
                'element' => array(
                    'prefix' => 'Workset_Form_Element',
                    'path' => 'Workset/Form/Element'
                )
            );
        }

        if (isset($options['ErrorMessages'])) {
            if (empty($options['ErrorMessages'])) {
                unset($options['ErrorMessages']);
            }
        } else {
            $options['ErrorMessages'] = array(
                self::ERROR_CODE
            );
        }

		parent::__construct($options);
	}

    /**
     * Устанавливает транслятор и контент для перевода
     *
     * @param  $translator
     * @param array $translation
     * @return void
     */
    public function setTranslator($translator = null, $translation = array()) {
        parent::setTranslator($translator);

        if ($translation && null !== $this->_translator && !$this->translatorIsDisabled()) {

           $args = func_get_args();

           for ($i = 1, $cnt = count($args); $i < $cnt; $i++) {

               if (!isset($args[$i]['content']) || !is_array($args[$i]['content'])) {
                   $args[$i] = array('content' => $args[$i]);
               }

               $this->_translator->addTranslation($args[$i]);
           }
        }
    }

    public function getCustomMessages($name = null, $formMessages = false)
    {
        if (false == $formMessages) {
            $messages = array_combine($this->_errorMessages, $this->_getErrorMessages());
        } else {
            $tmp = array_combine($this->_errorMessages, $this->_getErrorMessages());

            $this->clearErrorMessages();

            $messages = $tmp + $this->getMessages();

            $this->setErrorMessages($tmp);
        }

        if (null !== $name) {
            return isset($messages[$name]) ? $messages[$name] : null;
        }

        return $messages;

    }

	/**
	 * Задание объекта распределения прав доступа.
	 * 
	 * @param Zend_Acl $oAcl
	 * @return Workset_Form
	 */
	public function setAcl(Zend_Acl $oAcl) {
		$this->_oAcl = $oAcl;

		return $this;
	}

	/**
	 * Получение объекта распределения прав доступа.
	 * 
	 * @return Zend_Acl
	 */
	public function getAcl() {
		return $this->_oAcl;
	}

	/**
	 * Задание объекта, идентифицируещего пользователя.
	 * 
	 * @param mixed $oUser
	 * @return Workset_Form
	 */
	public function setUser($oUser) {
		$this->_oUser = $oUser;

		return $this;
	}

	/**
	 * Получение объекта, идентифицируещего пользователя.
	 * 
	 * @return mixed
	 */
	public function getUser() {
		return $this->_oUser;
	}

	/**
	 * Перехват задание некоторых аттрибутов формы.
	 *
	 * @param  string $key
	 * @param  mixed $value
	 * @return Zend_Form
	 */
	public function setAttrib($key, $value) {
		// Вызов родительского метода.
		parent::setAttrib($key, $value);

		// Если задаётся идентфикатор формы, то пробуем проверить права доступа.
		if ($key == 'id' && ($oAcl = $this->getAcl()) && $oAcl->has($value))
		{
			$oUser = $this->getUser();

			if ($oAcl->isAllowed($oUser,$value,'read')) {
				$this->_bFormIsReadOnly = !$oAcl->isAllowed($oUser,$value,'write');
			}
			else {
				throw new Workset_Acl_Exception('You do not have rights to access to this form.');
			}
		}

		// Возращаем ссылку на самих себя, для использования цепочек вызовов.
		return $this;
	}

	/**
	 * Перекрытие стандартного метода добавления поля.
	 *
	 * @param  string|Zend_Form_Element $element
	 * @param  string $name
	 * @param  array|Zend_Config $options
	 * @return Zend_Form
	 */
	public function addElement($element, $name = null, $options = null) {
		// Вызов родительского метода.
		parent::addElement($element, $name, $options);

		// Получение названия поля.
		if (null === $name && $element instanceof Zend_Form_Element) {
			$name = $element->getName();
		}

		// Проверяем поле по его имени на предмет наличия прав на доступ к нему.
		if (($oAcl = $this->getAcl()) && $oAcl->has($name)) {
			$oUser = $this->getUser();

			if ($oAcl->isAllowed($oUser,$name,'read')) {
				// Если поле доступно только для чтения, то замораживаем его и заме-
				// няем валидаторы на проверку отсутствия изменений.
				if ($this->_bFormIsReadOnly || !$oAcl->isAllowed($oUser,$name,'write')) {
					$oElement = $this->getElement($name);
					$mValue = $oElement->getValue();

					$oElement->setAttrib('disabled','disabled');
					$oElement->clearValidators();
					$oElement->addValidator('Zend_Identical',false,array('token' => $mValue));
				}
			}
			else {
				$this->removeElement($name);
			}
		}

		// Возращаем ссылку на самих себя, для использования цепочек вызовов.
		return $this;
	}

    /**
     * @param  $listId - ID списка
     * @param  $element - начальный элемент списка (должен быть добавлен в форму)
     * @param  $maxItems - максимальное значение элементов списка
     * @param  $nextItem - выводить дополнительно еще 1 пустой элемент списка после сохранения формы
     * @return Workset_Form
	 *
	 * Устанавливает именованный список элементов
     *  
     */
    public function setListElements($listId, $element, $maxItems = 0) {
        if (!$element instanceof Zend_Form_Element) {
            $element = $this->getElement($element);
        }

        if ($element instanceof Zend_Form_Element) {
			$element->setBelongsTo($listId);
			
            $this->_listElements[$listId] = array(
                'items' => array($element),
                'element' => $element,
				'maxItems' => $maxItems
            );
        }
		return $this;
    }

    /**
     * @param  string $listId
     * @param  Zend_Form_Element|string $element
     * @return Workset_Form
     *
     * Добавляет элемент в именованный список
     */
    public function addElementToList($listId, $value = null, $key = null) {
        if (isset($this->_listElements[$listId])) {
           $pos = count($this->_listElements[$listId]['items']);
		   if ($this->_listElements[$listId]['maxItems'] > 0 && $pos >= $this->_listElements[$listId]['maxItems']) {
			   return $this;
		   }

		   $firstElement = $this->_listElements[$listId]['element'];
           if (null !== $key) {
               $name = $key;
           } else {
               $name = $firstElement->getName().$pos;
           }

		   $currentElement = clone $firstElement;
           $currentElement->setName($name);
           $currentElement->setOrder($currentElement->getOrder() + $pos);
           if (null !== $value) {
               $currentElement->setValue((string)$value);
           }

           $this->addElement($currentElement);
           $this->_listElements[$listId]['items'][] = $currentElement;
        }
		return $this;
    }

	/**
	 * @param  $listId
	 * @param  $source
	 * @return Workset_Form
	 *
	 * Добавляет массив элементов в именованный список
	 */
	public function addElementsToList($listId, $source) {
		if (!is_array($source)) {
			$source = (array)$source;
		}

		if (isset($this->_listElements[$listId])) {
            if (count($source) < 2) {
                return $this;
            }
            array_shift($source);
			
			foreach ($source as $key => $value) {
				$this->addElementToList($listId, $value, $key);
			}
		}
		return $this;
	}

    public function addActionParams($params) {
        $action = $this->getAction();
		if (!$action) {
			$action = Zend_Controller_Front::getInstance()->getRequest()->getRequestUri();
		}

        if (false !== strpos($action, '?')) {
            $actionParts = explode('?', $action);
            if (!empty($actionParts[1])) {
                parse_str($actionParts[1], $defaultParams);
                $params = array_merge($defaultParams, $params);
            }
            $action = $actionParts[0].'?'.http_build_query($params);
        } else {
            $action .= '?'.http_build_query($params);
        }
        $this->setAction($action);

        return $this;
    }
	
}